package org.peerjs.app;

import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import org.peerjs.IPeerInputStream;
import org.peerjs.Peer;
import org.peerjs.configuration.IceServerOption;
import org.peerjs.configuration.PeerOptions;
import org.peerjs.configuration.VideoOption;
import org.peerjs.event.UiThreadEventRunContext;
import org.peerjs.google.webrtc.AudioHardwareOption;
import org.peerjs.google.webrtc.CapturerStreamSource;
import org.peerjs.log.AndroidLogFactory;
import org.peerjs.log.PLogFactory;
import org.peerjs.rtc.RtcBinding;
import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.EglBase;
import org.webrtc.Logging;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoFrame;
import org.webrtc.VideoSink;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author ht
 */
public class MainPeerActivity extends AppCompatActivity {
    private static final String TAG = MainPeerActivity.class.getSimpleName();
    private SurfaceViewRenderer pipVideo;
    private SurfaceViewRenderer localVideo;

    private TextView txtId;
    private TextView txtStatus;
    private TextView txtFriendId;
    private Peer peer;
    private EglBase eglBase;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_peer);
        txtId = findViewById(R.id.txt_id);
        txtStatus = findViewById(R.id.txt_status);
        txtFriendId = findViewById(R.id.txt_friend_id);
        txtStatus.setTextColor(Color.GRAY);

        pipVideo = findViewById(R.id.pip_video_view);
        localVideo = findViewById(R.id.local_video_view);

        eglBase = EglBase.create();

        pipVideo.init(eglBase.getEglBaseContext(), null);
        pipVideo.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);

        localVideo.init(eglBase.getEglBaseContext(), null);
        localVideo.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);

        pipVideo.setZOrderMediaOverlay(true);
        pipVideo.setEnableHardwareScaler(true /* enabled */);

        remoteProxyRenderer.setTarget(pipVideo);
        localProxyVideoSink.setTarget(localVideo);
        remoteSinks.add(remoteProxyRenderer);

        findViewById(R.id.btn_call).setOnClickListener((v) -> {
            call();
        });
        findViewById(R.id.btn_audio).setOnClickListener((v) -> {
            toggleAudio();
        });
        Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
            Log.e("Unhandled-Exception", "Error", e);
            assert defaultHandler != null;
            defaultHandler.uncaughtException(t, e);
        });
    }

    private void call() {
        String dstId = txtFriendId.getText().toString();
        peer.call(dstId, this::createStream);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("onDestroy", "onDestroy: " + (peer == null));
        if (peer != null) {
            Log.e("onDestroy", "onDestroy: " + peer.isOpen());
            peer.destroy();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (peer != null && peer.isOpen()) {
            return;
        }
        PLogFactory.initLogFactory(new AndroidLogFactory());
        String idStr = new Random().nextInt(3) + "";
        peer = new Peer(idStr,//"150b7441-b8bc-49a4-b9aa-ca2bec6b513a",
                new PeerOptions.Builder()
                        .host("192.168.0.104")
                        .port(9000)
                        .key("peerjs")
                        .path("myapp")
                        .media(true, new VideoOption.Builder().hwAcceration(false).frameRate(15).build())
                        .platform("android")
                        .rtcConfiguration()
                        .iceServers(IceServerOption.newBuilder().urls("stun:stun.l.google.com:19302").username("").password("").build())
                        .peerOptionBuilder()
                        .build(), new UiThreadEventRunContext());
        peer.onOpen((id) -> {
            txtStatus.setTextColor(Color.GREEN);
            txtStatus.setText("Connected");
            txtId.setText(id);
        });
        peer.onClose((evt) -> {
            if (peer.isOpen()) {
                txtStatus.setTextColor(Color.GRAY);
                txtStatus.setText("Not connected");
            }
        });
        peer.onError((error) -> {
            Log.e("PEER", error.getType().toString(), error.getCause());
        });
        peer.onCall((call) -> {
            Log.e("CALL", "电话收到: " + call.getInitiatorId());
            call.answer(createStream());
        });
        new Thread(() -> {
            peer.establish();
        }).start();

    }

    /**
     * 准备摄像头
     *
     * @return
     */
    private VideoCapturer createVideoCaptures() {
        if (Camera2Enumerator.isSupported(this)) {
            return createCameraCaptures(new Camera2Enumerator(this));
        } else {
            return createCameraCaptures(new Camera1Enumerator(true));
        }
    }

    private VideoCapturer createCameraCaptures(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();

        // 首先，试着找到前置摄像头
        Log.d(TAG, "寻找前置摄像头。");
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "创建前置摄像头捕捉器。");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        // 找不到前置摄像头，试试别的
        Log.d(TAG, "寻找其他相机。");
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "正在创建其他相机捕捉器。");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    private @Nullable
    VideoCapturer createScreenCaptures() {
        return createVideoCaptures();
    }

    private IPeerInputStream createStream() {
        return new CapturerStreamSource() {
            @Override
            protected void onBind(RtcBinding rtcBinging) {
                rtcBinding = rtcBinging;
                //rtcPeerConnection.on
                remoteSinks.add(remoteProxyRenderer);
            }

            @Override
            protected VideoSink getLocalVideoRender() {
                return localProxyVideoSink;
            }

            @Override
            protected List<VideoSink> getRemoteVideoRenderers() {
                return remoteSinks;
            }

            @Override
            protected VideoCapturer createVideoCapturer() {
                return createScreenCaptures();
            }

            @Override
            protected EglBase getEglBase() {
                return eglBase;
            }

            @Override
            protected PeerConnectionFactory.Options getPeerConnectionFactoryOptions() {
                return new PeerConnectionFactory.Options();
            }

            @Override
            protected AudioHardwareOption getAudioHardwareOption() {
                return new AudioHardwareOption.Builder().build();
            }

            @Override
            protected boolean isVideoEnabled() {
                return true;
            }

            @Override
            protected Context getAppContext() {
                return MainPeerActivity.this.getApplicationContext();
            }
        };
    }

    private final ProxyVideoSink remoteProxyRenderer = new ProxyVideoSink();
    private final ProxyVideoSink localProxyVideoSink = new ProxyVideoSink();
    private final List<VideoSink> remoteSinks = new ArrayList<>();

    private void toggleAudio() {
        //rtc.setAudioEnabled(!rtc.isAudioEnabled());
        rtcBinding.setAudioEnabled(!rtcBinding.isAudioEnabled());
    }

    private RtcBinding rtcBinding;

    private static class ProxyVideoSink implements VideoSink {
        private VideoSink target;

        @Override
        synchronized public void onFrame(VideoFrame frame) {
            if (target == null) {
                Log.d("activity", "由于目标为空，在代理中删除帧。");
                return;
            }

            target.onFrame(frame);
        }

        synchronized public void setTarget(VideoSink target) {
            this.target = target;
        }
    }
}